package fast.mvc;

import fast.data.CharacterClass;
import fast.data.Spec;
import fast.data.Stat;
import fast.loader.CharacterNotFoundException;
import fast.loader.Enchant;
import fast.loader.EnchantDB;
import fast.loader.Gem;
import fast.loader.GemDB;
import fast.loader.Item;
import fast.loader.ItemEquipped;
import fast.loader.Profession;
import fast.loader.Race;
import fast.loader.Reforge;
import fast.loader.ReforgeDB;
import fast.loader.Slot;
import fast.loader.Socket;
import fast.loader.SocketBonus;
import fast.ranking.EP;
import fast.ranking.ItemCompare;

import java.awt.Toolkit;
import java.awt.datatransfer.StringSelection;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.ItemEvent;
import java.awt.event.ItemListener;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Set;

import javax.swing.JButton;
import javax.swing.JCheckBox;
import javax.swing.JComboBox;
import javax.swing.JComponent;

public class Controller {

    private View view;
    private LoadProfileView loadProfileView;
    private Model model;

    public Controller(){
        this.model = new Model();
        this.view = new View();

        addListener();
        view.getIgnoreExp().setSelected(model.getSettings().isIgnoreExp());
        
        model.getEquipment().addPropertyChangeListener( new EquipmentListener() );
        
        showItemsInView();
    }
    
    public void showView(){
        this.view.setVisible(true);
    }

    private void addListener(){
        this.view.setCalcListener(new CalcListener());
        this.view.setLoadListener(new LoadListener());
        this.view.setSaveListener(new SaveListener());
        this.view.setLoadProfileListener(new LoadProfileListener());
        this.view.setAutoReforgeListener(new AutoReforgeListener());
        this.view.setAutoGemListener(new AutoGemListener());
        for(ItemSlotView isv : this.view.getItemMap().values()){
        	isv.setShowItemChangeListener(new ShowItemPanelListener());
        	isv.setEnchantListener(new EnchantListener());
        	isv.setGemListener(new GemListener());
        	isv.setReforgeListener(new ReforgeListener());
        	isv.setUpgradeListener(new UpgradeListener());
        }
        this.view.setSettingsListener(new SettingsListener());
        this.view.setChangeSpecListener(new ChangeSpecListener());
        this.view.setAltSpecListener(new AltSpecListener());
        this.view.setPlayerListener(new PlayerListener());
        this.view.setExportReforge(new ExportReforgeListener());
        this.view.setReforgeTest(new ReforgeTestListener());
        this.view.setBloodfang(new CalcBloodfangListener());
    }
    
    private void showItemsInView(){
    	for( Slot slot : model.getEquipment().keySet()){
    		ItemEquipped ie = model.getEquipment().get(slot);
    		ItemSlotView isv = view.getItemMap().get(slot);
    		
    		if(ie.getItem() != null){
	        	String enchant = "N";
	        	if(model.getEquipment().get(slot).getEnchant() != null)
	        		enchant = ie.getEnchant().getName();
	
	        	isv.setIcon(ie.getItem().getIcon());
	        	if(slot.isEnchantable())
	        		isv.setEnchant(enchant);
	        	
	        	Socket[] slotColors = ie.getItem().getGemSlot();
	        	Gem[] gems = ie.gem;
	        	for(int j = 0;j < gems.length; j++){
	        		if(gems[j] != null)
	        			isv.setGem(j, gems[j].getIcon());
	        		else
	        			isv.setGemSlot(j, slotColors[j]);
	        	}
	        	Reforge r = ie.getReforge();
	        	if(r != null)
	        		isv.setReforge(r.getFrom(), r.getTo());
	        	else
	        	isv.setReforge(null, null);
	        	if(ie.getItem().isUpgradable())
	        		isv.setUpgrade(ie.getItem().getUpgradeLevel(),ie.getItem().getMaxUpgradeLevel());
	        	else{
	        		isv.setUpgrade(0,ie.getItem().getMaxUpgradeLevel());
	        		isv.getUpgrade().setVisible(false);
	        	}
	        	isv.setInfo(ie.getItem().getItemLevel(), ie.getItem().getSet());
	        	isv.setTooltip(ie.toString());
    		}else{
    			isv.setIcon("inv_misc_questionmark");
    			isv.getEnchant().setVisible(false);
    			isv.getReforge().setVisible(false);
    			isv.getUpgrade().setVisible(false);
    			isv.setInfo(0,"");
    			for(JButton gemButton : isv.getGems()){
    				gemButton.setVisible(false);
    			}
    		}
        }
    }
    
    private void calcAndShow(){
    	Thread thread = new Thread(){
		    public void run(){
		    	
		    	double oldDPS = model.getDPS();
		    	model.calcFast();
		        view.setDPS(String.valueOf(Math.round(model.getDPS()*10.)/10.));
		        view.setStats(model.getEP(),model.getStats());
		        
		        view.showBreakdownPanel(model.getDpsBreakdown());
		        
		        double diff = model.getDPS() - oldDPS;
				String output = "DPS: " + String.valueOf(Math.round(model.getDPS()*10.)/10.);
				output += " (";
				if(diff >= 0)
					output += " +";
				output += String.valueOf(Math.round(diff*10.)/10.) + ")";
				View.addConsole(output);
		    }
		};
		thread.start();
    }
    
    private void setActiveSpec(Spec spec){
    	view.setActiveSpec(spec.name());
    	view.showSpecPanel(spec);
    	model.setSettings(new Settings(spec));
    	System.out.println(model.getSettings().getActiveSettings());
    	view.getSpecPanel().setOptions(model.getSettings().getActiveSettings());
    	
    	for(String opt : view.getSpecPanel().getOptions().keySet()){
    		JComponent component = view.getSpecPanel().getOptions().get(opt);
    		if(component instanceof JCheckBox){
    			((JCheckBox) component).addItemListener(new SpecSettingsListener(opt));
    		}else if(component instanceof JComboBox<?>){
    			((JComboBox<?>) component).addItemListener(new SpecSettingsListener(opt));
    		}
    	}
    }

    class CalcListener implements ActionListener {
        public void actionPerformed(ActionEvent e) {
            System.out.println("calc");
            calcAndShow();
        }
    }
    
    class CalcBloodfangListener implements ActionListener {
        public void actionPerformed(ActionEvent e) {
            System.out.println("calc bloodfang");
//            Thread thread = new Thread(){
//    		    public void run(){
    		    	
    		    	//double oldDPS = model.cycle.get_dps();
    		    	model.calcBloodfang();
    		    	model.bf.dps = model.bf.cycle.get_dps();
    		    	
    		    	System.out.println(model.bf.dps);
    		        view.setDPS(String.valueOf(Math.round(model.bf.dps*10.)/10.));
    		        model.bf.ep = model.bf.calc_ep();
    		        
    		        model.bf.ep_procs = model.bf.calc_ep_procs();
    		        
    		        HashMap<String,Double> eps = new HashMap<String,Double>(model.bf.ep);
    		        for(String st : model.bf.ep_procs.keySet()){
    		        	double[] val = model.bf.ep_procs.get(st);
    		        	for(int u = 0; u < val.length; u++){
    		        		String name = st;
    		        		if(u > 0)
    		        			name+= "/"+u;
    		        		eps.put(name, val[u]);
    		        	}
    		        }
    		        
    		        view.setStats(eps,model.getStats());
    		        
    		        //view.showBreakdownPanel(model.getDpsBreakdown());
    		        
//    		        double diff = model.cycle.get_dps() - oldDPS;
//    				String output = "DPS: " + String.valueOf(Math.round(model.cycle.get_dps()*10.)/10.);
//    				output += " (";
//    				if(diff >= 0)
//    					output += " +";
//    				output += String.valueOf(Math.round(diff*10.)/10.) + ")";
//    				View.addConsole(output);
//    		    }
//    		};
//    		thread.start();
        }
    }
    
    class LoadListener implements ActionListener {
        public void actionPerformed(ActionEvent e) {
        	try {
        		System.out.println("load");
        		model.reset();
        		showItemsInView();
				model.loadCharacter(view.getRegion(),view.getServer(),view.getPlayer());
				model.getEquipment().addPropertyChangeListener( new EquipmentListener() );
	            View.addLog("Class: " + model.getLoader().getBase().getCharacterClass().name());
	            View.addLog("Spec: " + model.getLoader().getBase().getSpec().name());
	            
	            setActiveSpec(model.getLoader().getBase().getSpec());
	            
	            showItemsInView();
	            
	            calcAndShow();
        	} catch (CharacterNotFoundException e1) {
				View.addConsole(e1.getMessage());
				view.getSidePanel().setViewportView(null);
				view.resetStats();
        	}
        }
    }
    
    class SaveListener implements ActionListener {
        public void actionPerformed(ActionEvent e) {
            System.out.println("save");
            model.saveCharacter();
        }
    }
    
    class LoadProfileListener implements ActionListener {
    	public void actionPerformed(ActionEvent e) {
    		System.out.println("load profile");
    		loadProfileView = new LoadProfileView(model.getLoader().getProfiles());
    		loadProfileView.setLoadListener(new LoadProfile2Listener());
    		loadProfileView.setVisible(true);
    	}
    }
    
    class LoadProfile2Listener implements ActionListener {
    	public void actionPerformed(ActionEvent e) {
    		String selected = loadProfileView.getSelectedProfile();
    		System.out.println(selected);
    	}
    }
    
    class ShowItemPanelListener implements ActionListener {
		public void actionPerformed(ActionEvent e) {
        	JButton isv = (JButton) e.getSource();
        	ItemSlotView v = (ItemSlotView) isv.getParent();
            Slot slot = v.getSlot();
            view.newItemPanel();
            
            ArrayList<Item> itemsForSlot = model.getDatabase().findAllWithInventorySlot(slot);
            ArrayList<Item> items = new ArrayList<Item>();
            boolean isPlayerEng = model.getLoader().getBase().getProfessions().contains("Engineering");
            boolean hasChapter2 = model.hasChapter2();
            CharacterClass characterClass = model.getLoader().getBase().getCharacterClass();
            for(Item ie : itemsForSlot){
            	if(model.getEquipment().get(slot) != null && !model.getEquipment().get(slot).getItem().equals(ie) && ie.getUpgradeLevel() > 0 && !model.getSettings().isShowUpgrades()){
    				continue;
    			}
            	if(ie.getItemClass() == 4 && characterClass.getAllowedArmor() < ie.getItemSubClass())
            		continue;
            	if(ie.getItemClass() == 2 && !characterClass.getAllowedWeapons().contains(ie.getItemSubClass()))
            		continue;
            	// This is for wowhead, but now we use battlenetapi
            	//if(ie.getReqClass() != 0 && ((ie.getReqClass() & characterClass.getMask()) != characterClass.getMask()))
            	//	continue;
            	if(ie.getReqClass() != 0 && (ie.getReqClass() != characterClass.getId()))
            		continue;
            	// sort out set items from other classes
            	if(characterClass.getId() == 4 && (ie.getName().startsWith("Fire-Charm") || ie.getName().endsWith("Forest")))
            		continue;
            	if(ie.getReqSkill() == 202 && !isPlayerEng)
            		continue;

    			if(hasChapter2 && (slot == Slot.mainHand || slot == Slot.offHand)){
    				for(int i = 0; i < ie.gemSlot.length; i++){
    					if(ie.gemSlot[i] == Socket.hydraulic){
    						//System.out.println(ie.getName() + " has hydraulic");
    						ie.gemSlot[i+1] = Socket.prismatic;
    						break;
    					}else if(ie.gemSlot[i] == null){
    						if(			ie.getItemLevel() >= 502 
    								&& (ie.getId() != 87012 && ie.getId() != 87032 && !ie.getTag().contains("PVP")) 
    								&& i > 0 && ie.gemSlot[i-1] != Socket.prismatic){
    							ie.gemSlot[i] = Socket.prismatic;
    							break;
    						}
    					}
    				}
    			}else if(slot == Slot.mainHand || slot == Slot.offHand){
    				for(int i = 0; i < ie.gemSlot.length; i++){
    					if(ie.gemSlot[i] == Socket.hydraulic){
    						ie.gemSlot[i+1] = null;
    						break;
    					}
    				}
    			}
    			if(slot == Slot.waist) {
    				for(int i = 0; i < ie.gemSlot.length; i++){
    					if(ie.gemSlot[i] == Socket.prismatic)
    						break;
    					else if(ie.gemSlot[i] == null){
    						ie.gemSlot[i] = Socket.prismatic;
    						break;
    					}
    				}
    			}
            	items.add(ie);
            }
            ItemEquipped curItem = null;
            if(model.getEquipment().get(slot) != null) {
            	curItem = model.getEquipment().get(slot);
            }
            
    		ItemCompare<Item> ic = new ItemCompare<Item>(model,curItem,items,slot);
    		ArrayList<EP<Item>> itemep = ic.getComperator();
    		Iterator<EP<Item>> it = itemep.iterator();
    		
            view.getItemPanel().showItems(it,curItem);
            
            view.showItemPanel(slot);
            //int pos = view.getItemPanel().getSelectedPosition();
            //view.getSidePanel().getVerticalScrollBar().setValue(pos);
            
            HashMap<JButton,Item> items2 = view.getItemPanel().getItems();
            for(JButton b : items2.keySet()){
            	view.getItemPanel().addItemChangeListener(b,new ItemChangeListener(slot,items2.get(b).getSuggestion().getItemEquipped()));
            }
        }
    }
    
    class EnchantListener implements ActionListener {
		public void actionPerformed(ActionEvent e) {
			JButton isv = (JButton) e.getSource();
        	ItemSlotView v = (ItemSlotView) isv.getParent().getParent();
            Slot slot = v.getSlot();
            view.showEnchantPanel();
            
            String slotName = slot.name();
            if(slotName.endsWith("1") || slotName.endsWith("2")){
            	slotName = slotName.substring(0, slotName.length()-1);
            }else if(slotName.equals("mainHand") || slotName.equals("offHand")){
            	slotName = "weapon";
            }
            // load enchants
            Enchant curEnchant = model.getEquipment().get(slot).getEnchant();
            ItemEquipped item = model.getLoader().getEquipment().get(slot);
    		EnchantDB enchantDB = model.getLoader().getEnchantDB();
    		ArrayList<Enchant> items = new ArrayList<Enchant>();
    		boolean isPlayerEnchanter = model.getLoader().getBase().getProfessions().contains(Profession.Enchanting);
    		boolean isPlayerLeatherworker = model.getLoader().getBase().getProfessions().contains(Profession.Leatherworking);
    		boolean isPlayerInscripter = model.getLoader().getBase().getProfessions().contains(Profession.Inscription);
    		boolean isPlayerTailor = model.getLoader().getBase().getProfessions().contains("Tailoring");
    		for ( Enchant elem : enchantDB.getDB().values() ){
    			// enchanter only enchants
    			if(elem.isEnchanterOnly() && !isPlayerEnchanter){
    				continue;
    			}
    			if(elem.isLeatherworkerOnly() && !isPlayerLeatherworker){
    				continue;
    			}
    			if(elem.isInscripterOnly() && !isPlayerInscripter){
    				continue;
    			}
    			if(elem.isTailorOnly() && !isPlayerTailor){
    				continue;
    			}
    			if(curEnchant != elem && elem.getStat(Stat.exp) > 0 && model.getSettings().isIgnoreExp()){
    				continue;
    			}
    			if(elem.getSlotName().equals(slotName))
    				items.add(elem);

    		}
    		items.add(null);
    		ItemCompare<Enchant> ic = new ItemCompare<Enchant>(model,item,items,slot);
    		ArrayList<EP<Enchant>> itemep = ic.getComperator();
    		Iterator<EP<Enchant>> it = itemep.iterator();
            
            view.getEnchantPanel().showItems(it,curEnchant);
            HashMap<JButton,Enchant> items2 = view.getEnchantPanel().getItems();
            for(JButton b : items2.keySet()){
            	view.getEnchantPanel().addEnchantChangeListener(b,new EnchantChangeListener(slot,items2.get(b)));
            }
        }
    }
    
    class ReforgeListener implements ActionListener {
		public void actionPerformed(ActionEvent e) {
			JButton isv = (JButton) e.getSource();
        	ItemSlotView v = (ItemSlotView) isv.getParent();     
            Slot slot = v.getSlot();
            view.showReforgePanel();
            
            // load reforge
            Reforge curReforge = model.getEquipment().get(slot).getReforge();
            ItemEquipped item = model.getLoader().getEquipment().get(slot);
    		ArrayList<Reforge> items = new ArrayList<Reforge>();
    		for ( Stat from : Stat.possibleReforgeStats ){
    			for( Stat to: Stat.possibleReforgeStats ){
    				if(from == to) continue;
    				if(model.hasBlockStat(item.getItem(), from) && !model.hasBlockStat(item.getItem(), to)){
    					ItemEquipped ie = new ItemEquipped();
    					ie.setItem(item.getItem());
    					ie.setReforge(new Reforge(from,to));
    					items.add(new Reforge(from,to, ie.getReforgeValue()));
    				}
    			}
    		}
    		items.add(null);
    		ItemCompare<Reforge> ic = new ItemCompare<Reforge>(model,item,items,slot);
    		ArrayList<EP<Reforge>> itemep = ic.getComperator();
    		Iterator<EP<Reforge>> it = itemep.iterator();
            
            view.getReforgePanel().showItems(it,curReforge);
            HashMap<JButton,Reforge> items2 = view.getReforgePanel().getItems();
            for(JButton b : items2.keySet()){
            	view.getReforgePanel().addReforgeChangeListener(b,new ReforgeChangeListener(slot,items2.get(b)));
            }
        }
    }
    
    class UpgradeListener implements ActionListener {
		public void actionPerformed(ActionEvent e) {
			JButton isv = (JButton) e.getSource();
        	ItemSlotView v = (ItemSlotView) isv.getParent().getParent();     
            Slot slot = v.getSlot();
            ItemEquipped ie = model.getEquipment().get(slot);
            int upgrade_level = ie.getItem().getUpgradeLevel();
            if(upgrade_level == ie.getItem().getMaxUpgradeLevel())
            	upgrade_level = 0;
            else
            	upgrade_level++;
            int itemid = ie.getItem().getId();
            Item item = model.getDatabase().get(itemid, upgrade_level);
            if(slot == Slot.waist) {
				for(int i = 0; i < item.gemSlot.length; i++){
					if(item.gemSlot[i] == Socket.prismatic)
						break;
					else if(item.gemSlot[i] == null){
						item.gemSlot[i] = Socket.prismatic;
						break;
					}
				}
			}
            boolean hasChapter2 = model.hasChapter2();
            if(hasChapter2 && (slot == Slot.mainHand || slot == Slot.offHand)){
				for(int i = 0; i < item.gemSlot.length; i++){
					if(item.gemSlot[i] == Socket.hydraulic){
						//System.out.println(ie.getName() + " has hydraulic");
						item.gemSlot[i+1] = Socket.prismatic;
						break;
					}else if(item.gemSlot[i] == null){
						if(			item.getItemLevel() >= 502 
								&& (item.getId() != 87012 && item.getId() != 87032 && !item.getTag().contains("PVP")) 
								&& i > 0 && item.gemSlot[i-1] != Socket.prismatic){
							item.gemSlot[i] = Socket.prismatic;
							break;
						}
					}
				}
			}else if(slot == Slot.mainHand || slot == Slot.offHand){
				for(int i = 0; i < item.gemSlot.length; i++){
					if(item.gemSlot[i] == Socket.hydraulic){
						item.gemSlot[i+1] = null;
						break;
					}
				}
			}
            model.getEquipment().get(slot).setItem(item);
            model.getEquipment().setUpgrade(slot, upgrade_level);
        }
    }
    
    class GemListener implements ActionListener {
		public void actionPerformed(ActionEvent e) {
        	JButton isv = (JButton) e.getSource();
        	ItemSlotView v = (ItemSlotView) isv.getParent().getParent();
        	JButton[] gemButtons = v.getGems();
        	int gemSlot = -1;
        	for(int i = 0;i < gemButtons.length;i++){
        		if(gemButtons[i].equals(isv)){
        			gemSlot = i;
        			break;
        		}	
        	}
            Slot s = v.getSlot();
            view.showGemPanel();
            
            // load gems
            Gem curGem = model.getEquipment().get(s).getGem(gemSlot);
            ItemEquipped item = model.getLoader().getEquipment().get(s);
            Socket gemSlotColor = item.getItem().getGemSlot()[gemSlot];
    		GemDB gemDB = model.getLoader().getGemDB();
    		ArrayList<Gem> items = new ArrayList<Gem>();
    		boolean isPlayerJC = model.getLoader().getBase().getProfessions().contains(Profession.Jewelcrafting);
    		boolean isPlayerEng = model.getLoader().getBase().getProfessions().contains(Profession.Engineering);
    		for ( Gem elem : gemDB.getDB().values() ){
    			// 755 jewelcrafting
    			if(elem.getReqSkill() == 755 && !isPlayerJC){
    				continue;
    			}
    			else if(elem.getReqSkill() == 202 && !isPlayerEng){
    				continue;
    			}
    			if(elem.getStat(Stat.exp) > 0 && model.getSettings().isIgnoreExp()){
    				if(curGem != elem)
    					continue;
    			}
    			
    			if(gemSlotColor == Socket.meta && elem.getType() == Gem.META)
    				items.add(elem);
    			else if(gemSlotColor == Socket.cogwheel && elem.getType() == Gem.COGWHEEL)
    				items.add(elem);
    			else if(gemSlotColor == Socket.hydraulic && elem.getType() == Gem.HYDRAULIC)
    				items.add(elem);
    			else if(		(gemSlotColor != Socket.cogwheel && elem.getType() != Gem.COGWHEEL) 
    						&& (gemSlotColor != Socket.meta && elem.getType() != Gem.META)
    						&& (gemSlotColor != Socket.hydraulic && elem.getType() != Gem.HYDRAULIC)
    					)
    				items.add(elem);
    		}
    		items.add(null);
    		ItemCompare<Gem> ic = new ItemCompare<Gem>(model,item,items,s);
    		ArrayList<EP<Gem>> itemep = ic.getComperator();
    		Iterator<EP<Gem>> it = itemep.iterator();
            
            view.getGemPanel().showItems(it,curGem);
            HashMap<JButton,Gem> items2 = view.getGemPanel().getItems();
            for(JButton b : items2.keySet()){
            	view.getGemPanel().addGemChangeListener(b,new GemChangeListener(s,gemSlot,items2.get(b)));
            }
        }
    }
    
    class PlayerListener implements ActionListener {
		public void actionPerformed(ActionEvent e) {
			List<String> glyphs = model.getLoader().getBase().getGlyphs();
			Set<String> talentSet = model.getLoader().getBase().getTalents();
            view.showPlayerPanel(model.getLoader().getBase().getCharacterClass(),model.getLoader().getBase().getRace(),model.getLoader().getBase().getProfessions(),glyphs,talentSet);
            view.getPlayerPanel().addRaceChangeListener(new RaceChangeListener());
            
            view.getPlayerPanel().addProfessionChangeListener(new ProfessionChangeListener());
            view.getPlayerPanel().addGlyphChangeListener(new GlyphChangeListener());
            view.getPlayerPanel().addBuffChangeListener(new BuffChangeListener());
            view.getPlayerPanel().addTalentChangeListener(new TalentChangeListener());
        }
    }
    
    class ExportReforgeListener implements ActionListener {
		public void actionPerformed(ActionEvent e) {
			StringBuffer sb = new StringBuffer();
			for(Slot slot : model.getEquipment().keySet()){
				ItemEquipped ie = model.getEquipment().get(slot);
				if(ie != null && (slot != Slot.shirt || slot != Slot.tabard)){
					String string = slot.name();
					String s1 = Character.toUpperCase(string.charAt(0)) + string.substring(1, string.length());
					if(ie.getReforge() != null){
						if(s1.equals("Shoulder"))
							s1 = "Shoulders";
						else if(s1.equals("Finger1"))
							s1 = "Ring1";
						else if(s1.equals("Finger2"))
							s1 = "Ring2";
						else if(s1.equals("Wrist"))
							s1 = "Wrists";
						sb.append(s1 + ": " + ie.getReforge().getFrom().getReforgeName().replace(" ", "") + " -> " + ie.getReforge().getTo().getReforgeName().replace(" ", ""));
						sb.append("\n");
					}
				}
			}
			Toolkit.getDefaultToolkit().getSystemClipboard().setContents(
                    new StringSelection(sb.toString()), null);
        }
    }
    
    class ItemChangeListener implements ActionListener {
    	private Slot slot;
    	// here is the data used to set best gems and reforge
    	private ItemEquipped suggestion;
    	private Item item;
    	
    	public ItemChangeListener(Slot s,ItemEquipped item2){
    		slot = s;
    		suggestion = item2;
    		item = suggestion.getItem();
    	}
		public void actionPerformed(ActionEvent e) {
			ItemEquipped ie = new ItemEquipped();
			if(slot == Slot.waist) {
				for(int i = 0; i < item.gemSlot.length; i++){
					if(item.gemSlot[i] == Socket.prismatic)
						break;
					else if(item.gemSlot[i] == null){
						item.gemSlot[i] = Socket.prismatic;
						break;
					}
				}
			}
			boolean hasChapter2 = model.hasChapter2();
			if(hasChapter2 && (slot == Slot.mainHand || slot == Slot.offHand)){
				for(int i = 0; i < item.gemSlot.length; i++){
					if(item.gemSlot[i] == Socket.hydraulic){
						//System.out.println(ie.getName() + " has hydraulic");
						item.gemSlot[i+1] = Socket.prismatic;
						break;
					}else if(item.gemSlot[i] == null){
						if(			item.getItemLevel() >= 502 
								&& (item.getId() != 87012 && item.getId() != 87032 && !item.getTag().contains("PVP")) 
								&& i > 0 && item.gemSlot[i-1] != Socket.prismatic){
							item.gemSlot[i] = Socket.prismatic;
							break;
						}
					}
				}
			}else if(slot == Slot.mainHand || slot == Slot.offHand){
				for(int i = 0; i < item.gemSlot.length; i++){
					if(item.gemSlot[i] == Socket.hydraulic){
						item.gemSlot[i+1] = null;
						break;
					}
				}
			}
			
			if(model.getLoader().getBase().getProfessions().contains(Profession.Blacksmithing) && (slot == Slot.wrist || slot == Slot.hands)) {
				for(int i = 0; i < item.gemSlot.length; i++){
					if(item.gemSlot[i] == Socket.prismatic)
						break;
					else if(item.gemSlot[i] == null){
						item.gemSlot[i] = Socket.prismatic;
						break;
					}
				}
			}else if(slot == Slot.wrist || slot == Slot.hands){
				for(int i = 0; i < item.gemSlot.length; i++){
					if(item.gemSlot[i] == Socket.prismatic){
						item.gemSlot[i] = null;
						break;
					}
				}
			}
			ie.setItem(item);
			ie.setEnchant(model.getBestEnchant(slot));
			
			for(int i = 0;i < item.gemSlot.length;i++){
				ie.setGem(i, suggestion.getGem(i));
			}
			ie.setReforge(suggestion.getReforge());
			model.getEquipment().put(slot, ie);
		}
    }
    
    class EnchantChangeListener implements ActionListener{
    	private Slot slot;
    	private Enchant enchant;
    	
    	public EnchantChangeListener(Slot s,Enchant e){
    		slot = s;
    		enchant = e;
    	}
		public void actionPerformed(ActionEvent e) {
			System.out.println("change enchant");
			model.getEquipment().setEnchant(slot, enchant);
		}
    }
    
    class GemChangeListener implements ActionListener{
    	private Slot slot;
    	private int gemSlot;
    	private Gem gem;
    	
    	public GemChangeListener(Slot s,int gs,Gem g){
    		slot = s;
    		gemSlot = gs;
    		gem = g;
    	}
		public void actionPerformed(ActionEvent e) {
			System.out.println("change gem");
			model.getEquipment().setGem(slot, gemSlot, gem);
		}
    }
    
    class ReforgeChangeListener implements ActionListener{
    	private Slot slot;
    	private Reforge reforge;
    	
    	public ReforgeChangeListener(Slot s,Reforge r){
    		slot = s;
    		reforge = r;
    	}
		public void actionPerformed(ActionEvent e) {
			System.out.println("change reforge");
			model.getEquipment().setReforge(slot, reforge);
		}
    }
    
    class ReforgeTestListener implements ActionListener {
    	

    	public void actionPerformed(ActionEvent e) {
    		model.reforge();
    	}
    }
    
    class AutoReforgeListener implements ActionListener {
    	
    	AutoReforgeTask task;
    	
    	public void actionPerformed(ActionEvent e) {
			task = new AutoReforgeTask(model,view,model.getRaceWeaponSpecilizationReduction(Slot.mainHand));
	        task.execute();
	        view.getAutoReforgeButton().setEnabled(false);
	        view.getLoadingImage().setVisible(true);
    		
    		
    		/*JSONObject json = new JSONObject();
    		
    		JSONObject ep = new JSONObject();
    		JSONObject cap = new JSONObject();
    		JSONObject ratings = new JSONObject();
    		JSONArray items = new JSONArray();
    		try {
				ep.put("crit_rating", model.fast.getEP().get("crit"));
				ep.put("mastery_rating", model.fast.getEP().get("mastery"));
	    		ep.put("haste_rating", model.fast.getEP().get("haste"));
	    		ep.put("expertise_rating", model.fast.getEP().get("exp"));
	    		ep.put("mh_expertise_rating", model.fast.getEP().get("mh_exp"));
	    		ep.put("oh_expertise_rating", model.fast.getEP().get("oh_exp"));
	    		ep.put("hit_rating", model.fast.getEP().get("white_hit"));
	    		ep.put("yellow_hit", model.fast.getEP().get("style_hit"));
	    		
	    		cap.put("yellowHitCap", 2550);
	    		cap.put("spellHitCap", 2550);
	    		cap.put("whiteHitCap", 9010);
	    		cap.put("mh_exp", 2550 - model.getRaceWeaponSpecilizationReduction(Slot.mainHand));
	    		cap.put("oh_exp", 2550 - model.getRaceWeaponSpecilizationReduction(Slot.offHand));
	    		
	    		System.out.println(cap);
	    		
	    		StatBlock rat = model.getEquipment().getStatsAsBlock();
	    		rat.subtract(model.getEquipment().getReforgingsAsBlock());
	    		ratings.put("haste_rating", rat.getStat(Stat.hst));
	    		ratings.put("mastery_rating", rat.getStat(Stat.mst));
	    		ratings.put("crit_rating", rat.getStat(Stat.cri));
	    		ratings.put("expertise_rating", rat.getStat(Stat.exp));
	    		ratings.put("hit_rating", rat.getStat(Stat.hit));
	    		
	    		for(Slot slot : model.getEquipment().keySet()){
	    			ItemEquipped ie = model.getEquipment().get(slot);
	    			JSONObject item = new JSONObject();
	    			item.put("id", slot);
	    			for(Stat stat : Stat.possibleReforgeStats){
	    				double value = ie.getItem().getStat(stat);
	    				if(value > 0){
	    					item.put(stat.getOtherReforgeName(), value);
	    				}
	    			}
	    			items.put(item);
	    		}
	    		
	    		json.put("ep", ep);
	    		json.put("cap", cap);
	    		json.put("ratings", ratings);
	    		json.put("items", items);
		        RemoteReforge rr = new RemoteReforge(json.toString());
		        JSONObject result = new JSONObject(rr.getResult());
		        
		        for(Slot slot : Slot.possibleSlots){
		        	if(slot == Slot.shirt || slot == Slot.tabard) continue;
		        	try{
			        	int reforgeid = result.getInt(slot.name());
			        	System.out.println(reforgeid);
			        	Reforge r = null;
			        	if(reforgeid > 0){
			        		Stat from = ReforgeDB.getReforgeFrom(reforgeid);
				        	Stat to = ReforgeDB.getReforgeTo(reforgeid);
				        	r = new Reforge(from,to);
			        	}
			        	model.getEquipment().setAutoReforge(slot, r);
		        	}catch(JSONException js){
		        		System.out.println("error: " + slot);
		        		System.out.println(js.getMessage());
		        	}
		        }
		        model.getEquipment().fireAutoReforgeComplete();
			} catch (JSONException e1) {
				e1.printStackTrace();
			}*/
    	}
    }
    
    class AutoGemListener implements ActionListener {
    	public void actionPerformed(ActionEvent e) {
    		Gem bestMeta = model.getBestGem(Socket.meta);
    		Gem bestRed = model.getBestGem(Socket.red);
    		Gem bestYellow = model.getBestGem(Socket.yellow);
    		Gem bestBlue = model.getBestGem(Socket.blue);
    		Gem bestHydraulic = model.getBestGem(Socket.hydraulic);
    		double bestMetaEP = model.calcGemEP(bestMeta);
    		double bestRedEP = model.calcGemEP(bestRed);
    		double bestYellowEP = model.calcGemEP(bestYellow);
    		double bestBlueEP = model.calcGemEP(bestBlue);
    		double bestHydraulicEP = model.calcGemEP(bestHydraulic);
    		
    		Gem bestGem = bestRed;
    		if(bestYellowEP > model.calcGemEP(bestGem))
    			bestGem = bestYellow;
    		if(bestBlueEP > model.calcGemEP(bestGem))
    			bestGem = bestBlue;
    		double bestGemEP = model.calcGemEP(bestGem);
		
    		for(Slot slot : model.getEquipment().keySet()){
    			ItemEquipped ie = model.getEquipment().get(slot);
    			if(ie != null){
    				double gemMatchEP = 0.;
    				Socket[] gemSlot = ie.getItem().getGemSlot();
    				for(int i = 0;i < gemSlot.length;i++){
    					if(gemSlot[i] == Socket.meta){
    						gemMatchEP += bestMetaEP;
    					}else if(gemSlot[i] == Socket.red){
    						gemMatchEP += bestRedEP;
    					}else if(gemSlot[i] == Socket.yellow){
    						gemMatchEP += bestYellowEP;
    					}else if(gemSlot[i] == Socket.blue){
    						gemMatchEP += bestBlueEP;
    					}else if(gemSlot[i] == Socket.prismatic){
    						gemMatchEP += bestGemEP;
    					}else if(gemSlot[i] == Socket.hydraulic){
    						gemMatchEP += bestHydraulicEP;
    					}
    				}
        			double gemSocketEP = 0.;
        			if(ie.getItem().getSocketBonus() != null){
        				SocketBonus sb = ie.getItem().getSocketBonus();
        				for(Stat stat: Stat.possibleStats){
        					if(model.getEP().get(stat.getEpName()) != null)
        						gemSocketEP += model.getEP().get(stat.getEpName()) * sb.getStat(stat);
        				}
        			}
      
        			gemMatchEP += gemSocketEP;
        			
        			double gemOnlyBestEP = 0.;
        			//boolean gemOnlyBestMatch = true;
        			for(int i = 0;i < gemSlot.length;i++){
        				if(gemSlot[i] == Socket.meta){
        					gemOnlyBestEP += bestMetaEP;
    					}
    					else if(gemSlot[i] != null){
        					gemOnlyBestEP += bestGemEP;
        				}
        			}
        			if(gemOnlyBestEP > gemMatchEP){
        				for(int i = 0;i < gemSlot.length;i++){
        					if(gemSlot[i] == Socket.meta){
        						ie.setGem(i, bestMeta);
        					}else if(gemSlot[i] == Socket.hydraulic){
        						ie.setGem(i, bestHydraulic);
        					}
        					else if(gemSlot[i] != null){
        						ie.setGem(i, bestGem);
        					}
        				}
        			} else {
        				for(int i = 0;i < gemSlot.length;i++){
        					if(gemSlot[i] == Socket.meta){
        						ie.setGem(i, bestMeta);
        					}
        					else if(gemSlot[i] == Socket.red){
        						ie.setGem(i, bestRed);
        					}
        					else if(gemSlot[i] == Socket.yellow){
        						ie.setGem(i, bestYellow);
        					}
        					else if(gemSlot[i] == Socket.blue){
        						ie.setGem(i, bestBlue);
        					}
        					else if(gemSlot[i] == Socket.prismatic){
        						ie.setGem(i, bestGem);
        					}
        					else if(gemSlot[i] == Socket.hydraulic){
        						ie.setGem(i, bestHydraulic);
        					}
        				}
        			}
    			}
    		}
    		model.getEquipment().fireAutoGemComplete();
    	}
    }
    
    class SettingsListener implements ActionListener {
    	public void actionPerformed(ActionEvent e) {
    		System.out.println(e.getSource());
	    	if(e.getSource() == view.getIgnoreExp()){
	    		model.getSettings().setIgnoreExp(view.getIgnoreExp().isSelected());
	    	}else if(e.getSource() == view.getShowUpgrades()){
	    		model.getSettings().setShowUpgrades(view.getShowUpgrades().isSelected());
	    	}
    	}
    }
    
    class SpecSettingsListener implements ItemListener {
    	String option = "";
    	public SpecSettingsListener(String opt) {
			option = opt;
		}

    	public void itemStateChanged(ItemEvent e) {
    		if(e.getSource() instanceof JCheckBox){
    			JCheckBox box = (JCheckBox) e.getSource();
    			if(box.isSelected()){
    				System.out.println(option+ " active");
    				model.getSettings().toggle(option);
    			}else{
    				System.out.println(option+ " inactive");
    				model.getSettings().toggle(option);
    			}
    			calcAndShow();
    		}else if(e.getSource() instanceof JComboBox){
    			JComboBox<?> box = (JComboBox<?>) e.getSource();
    			if (e.getStateChange() == ItemEvent.SELECTED) {
	    			int value = (Integer) box.getSelectedItem();
	    			System.out.println("selected bg value: " + value);
	    			model.getSettings().setSettingsValue("bf_targets", value);
    			}
    		}
    	}
    }
    
    class ChangeSpecListener implements ActionListener {
    	public void actionPerformed(ActionEvent e) {
	    	Spec activeSpec = model.getLoader().getBase().getSpec();
	    	Set<String> activeTalents = model.getLoader().getBase().getTalents();
	    	List<String> activeGlyphs = model.getLoader().getBase().getGlyphs();
	    	Spec inactiveSpec = model.getLoader().getBase().getOffSpec();
	    	Set<String> inactiveTalents = model.getLoader().getBase().getOffTalents();
	    	List<String> inactiveGlyphs = model.getLoader().getBase().getOffGlyphs();
	    	model.getLoader().getBase().setSpec(inactiveSpec);
	    	model.getLoader().getBase().setTalents(inactiveTalents);
	    	model.getLoader().getBase().setGlyphs(inactiveGlyphs);
	    	model.getLoader().getBase().setOffSpec(activeSpec);
	    	model.getLoader().getBase().setOffTalents(activeTalents);
	    	model.getLoader().getBase().setOffGlyphs(activeGlyphs);
	    	
	    	setActiveSpec(inactiveSpec);
	    	
	    	calcAndShow();
    	}
    }
    
    class AltSpecListener implements ActionListener {
    	public void actionPerformed(ActionEvent e) {
	    	Spec activeSpec = model.getLoader().getBase().getSpec();
	    	Spec inactiveSpec = model.getLoader().getBase().getOffSpec();
	    	Spec newSpec = null;
	    	CharacterClass characterClass = model.getLoader().getBase().getCharacterClass();
	    	Spec[] characterSpecs = Spec.getSpecsByCharacterClass(characterClass);
    		for(Spec spec : characterSpecs){
    			if(activeSpec != spec && inactiveSpec != spec) {
    				newSpec = spec;
    				break;
    			}
    		}
	    	
	    	model.getLoader().getBase().setSpec(newSpec);
	    	setActiveSpec(newSpec);
	    	
	    	calcAndShow();
    	}
    }
    
    class RaceChangeListener implements ActionListener {
    	@SuppressWarnings("unchecked")
		public void actionPerformed(ActionEvent e) {
    		JComboBox<Race> raceList = null;
    		if(e.getSource() instanceof JComboBox){
    			raceList = (JComboBox<Race>) e.getSource();
    		}else
    			new Exception("raceList Error");
	    	Race race = (Race) raceList.getSelectedItem();
	    	model.getLoader().getBase().setRace(race);
	    	
	    	model.calcFast();
            view.setDPS(String.valueOf(Math.round(model.getDPS()*10.)/10.));
            view.setStats(model.getEP(),model.getStats());
    	}
    }
    
    class ProfessionChangeListener implements ActionListener {
    	public void actionPerformed(ActionEvent e) {
	    	
    		model.getLoader().getBase().setProfessions(view.getPlayerPanel().getSelectedProfessions());
    		if(model.getLoader().getBase().getProfessions().contains(Profession.Blacksmithing)){
    			model.getEquipment().addExtraSocket(Slot.wrist);
    			model.getEquipment().addExtraSocket(Slot.hands);
    		}else{
    			model.getEquipment().removeExtraSocket(Slot.wrist);
    			model.getEquipment().removeExtraSocket(Slot.hands);
    		}
    		if(!model.getLoader().getBase().getProfessions().contains(Profession.Enchanting)){
    			model.getEquipment().get(Slot.finger1).setEnchant(null);
    			model.getEquipment().get(Slot.finger2).setEnchant(null);
    		}
    		model.getEquipment().fireProfessionChangeComplete();
    		
	    	model.calcFast();
	    	view.setDPS(String.valueOf(Math.round(model.getDPS()*10.)/10.));
            view.setStats(model.getEP(),model.getStats());
    	}
    }
    
    class GlyphChangeListener implements ActionListener {
    	public void actionPerformed(ActionEvent e) {
    		JCheckBox glyph = (JCheckBox) e.getSource();
    		if(glyph.isSelected())
    			model.getLoader().getBase().addGlyph(glyph.getText());
    		else
    			model.getLoader().getBase().removeGlyph(glyph.getText());
    		
    		model.calcFast();
    		view.setDPS(String.valueOf(Math.round(model.getDPS()*10.)/10.));
            view.setStats(model.getEP(),model.getStats());
    	}
    }
    
    class BuffChangeListener implements ActionListener {
    	public void actionPerformed(ActionEvent e) {
    		JCheckBox box = (JCheckBox) e.getSource();
    		if(box.isSelected())
    			model.getLoader().getBase().addBuff(box.getText());
    		else
    			model.getLoader().getBase().removeBuff(box.getText());
    		
    		model.calcFast();
    		view.setDPS(String.valueOf(Math.round(model.getDPS()*10.)/10.));
            view.setStats(model.getEP(),model.getStats());
    	}
    }
    
    class TalentChangeListener implements ActionListener {
    	public void actionPerformed(ActionEvent e) {
    		model.getLoader().getBase().setTalents(view.getPlayerPanel().getTalentTree());
    		
    		model.calcFast();
    		view.setDPS(String.valueOf(Math.round(model.getDPS()*10.)/10.));
            view.setStats(model.getEP(),model.getStats());
    	}
    }

	class EquipmentListener implements PropertyChangeListener {
		public void propertyChange(PropertyChangeEvent evt) {
			double oldDPS = model.getDPS();
			model.calcFast();
			view.setDPS(String.valueOf(Math.round(model.getDPS()*10.)/10.));
			view.setStats(model.getEP(),model.getStats());
            showItemsInView();
            
			if(evt.getPropertyName().endsWith("enchant")){
				System.out.println("change: " + evt.getPropertyName());
				String[] arg = evt.getPropertyName().split("_");
				view.getItemMap().get(Slot.valueOf(arg[0])).getEnchant().doClick();
			}else if(evt.getPropertyName().endsWith("autogem")){
				System.out.println("change: " + evt.getPropertyName());
			}else if(evt.getPropertyName().endsWith("gem")){
				System.out.println("change: " + evt.getPropertyName());
				String[] arg = evt.getPropertyName().split("_");
				view.getItemMap().get(Slot.valueOf(arg[0])).getGems()[Integer.parseInt(arg[1])].doClick();
			}else if(evt.getPropertyName().endsWith("professionchange")){
				System.out.println("change: " + evt.getPropertyName());
			}else if(evt.getPropertyName().endsWith("autoreforge")){
				System.out.println("change: " + evt.getPropertyName());
			}else if(evt.getPropertyName().endsWith("reforge")){
				String[] arg = evt.getPropertyName().split("_");
				view.getItemMap().get(Slot.valueOf(arg[0])).getReforge().doClick();
			}else if(evt.getPropertyName().endsWith("upgrade")){
				System.out.println("change: " + evt.getPropertyName());
				String[] arg = evt.getPropertyName().split("_");
				if(view.getSidePanel().getViewport().getView() == view.getItemPanel())
					view.getItemMap().get(Slot.valueOf(arg[0])).getItem().doClick();
			}else{
				view.getItemMap().get(Slot.valueOf(evt.getPropertyName())).getItem().doClick();
			}
			double diff = model.getDPS() - oldDPS;
			String output = "DPS: " + String.valueOf(Math.round(model.getDPS()*10.)/10.);
			output += " (";
			if(diff >= 0)
				output += " +";
			output += String.valueOf(Math.round(diff*10.)/10.) + ")";
			View.addConsole(output);
		}
	}
}